/******************************************************************************
 * Filename : uc_file.c
 * Created  : 2009-10-01 by franco.yin
 * Description -
 *
 ******************************************************************************/

#ifdef __cplusplus
extern "C" {
#endif

#include "uc_file.h"
#include "uc_string.h"

char uc_path[UC_BUF_PATH_LEN] = "";

int
ucfile_exist(char *fname)
{
	struct stat buf;
	return stat(fname, &buf) ? false : true;
}

int
ucfile_size(const char *fname)
{
	struct stat buf;

	if(fname == NULL || stat(fname, &buf) < 0) {
		return -1;
	}
    return (int)buf.st_size;
}

UC_S64
ucfile_size_big(const char *fname)
{
	struct stat buf;

	if(fname == NULL || stat(fname, &buf) < 0) {
		return -1;
	}
    return buf.st_size;
}

char *
ucfile_pwd()
{
	static char buf[UC_BUF_LOG_LEN];

	buf[0] = 0;
	if(getcwd(buf, sizeof(buf) - 1) == NULL) {
		return NULL;
	}

	return buf;
}

int
ucfile_cp(char *src, char *dst)
{
	int sfd, dfd, ret = true;
	size_t len;
	ssize_t slen = 0;
	char buf[UC_BUF_LINE_LEN];

	if (ucfile_exist(src) == false) {
		sfd = open(src, O_WRONLY | O_TRUNC | O_CREAT | O_SYNC, S_IRWXU | S_IRWXG | S_IRWXO);
		close(sfd);
		dfd = open(dst, O_WRONLY | O_TRUNC | O_CREAT | O_SYNC, S_IRWXU | S_IRWXG | S_IRWXO);
		close(dfd);
		return true;
	}
	if ((sfd = open(src, O_CREAT | O_RDONLY)) < 0) {
		return false;
	}

	if ((dfd = open(dst, O_WRONLY | O_TRUNC | O_CREAT | O_SYNC, S_IRWXU | S_IRWXG | S_IRWXO)) < 0) {
		close(sfd);
		return false;
	}

	while (1) {
		len = read(sfd, buf, UC_BUF_LINE_LEN);
		if (len == 0) {
			break;
		} else if (len < 0) {
			ret = false;
			break;
		}
		slen = (ssize_t)len;
		if (write(dfd, buf, len) != slen) {
			ret = false;
			break;
		}
	}
	close(sfd);
	close(dfd);
	return ret;
}

int
ucfile_log_file_rebuild(const char *fname, int max_len)
{
	char file_name_bak[UC_BUF_PATH_LEN];
	int file_size = ucfile_size(fname);

	if(max_len <= 0) {
		return OK;
	}

	if(file_size >= max_len) {
		sprintf(file_name_bak, "%s.bak", fname);
		remove(file_name_bak);

		uc_system("mv \"%s\" \"%s\"", fname, file_name_bak);

		uc_info("backup new file[%s] file size[%d]\n", file_name_bak, file_size);
	}

	return OK;
}

int
ucfile_log_file_rebuild_keep_tail(const char *fname, int max_len, int keep_line)
{
	char file_name_bak[UC_BUF_PATH_LEN];
	int file_size = ucfile_size(fname);

	if(max_len <= 0) {
		return OK;
	}

	if(file_size >= max_len) {
		sprintf(file_name_bak, "%s.bak", fname);
		remove(file_name_bak);

		uc_system("mv \"%s\" \"%s\"", fname, file_name_bak);
		uc_system("tail -n %d \"%s\" >\"%s\"", keep_line, file_name_bak, fname);
		remove(file_name_bak);

		if(ucfile_size(fname) >= max_len) {
			remove(fname);
			keep_line = 0;
		}

		uc_info("file[%s] file size[%d] only keep tail[%d]\n", fname, file_size, keep_line);
	}

	return OK;
}

char *
ucfile_get_exe_full_path()
{
	static char buf[UC_BUF_LOG_LEN];
	int ret = 0;

	ret = readlink("/proc/self/exe", buf, UC_BUF_LOG_LEN);
	if (ret < 0 || ret >= UC_BUF_LOG_LEN ) {
		return NULL;
	}

	buf[ret] = '\0';
	return buf;
}

char *
ucfile_get_exe_path()
{
	static char buf[UC_BUF_LOG_LEN];
	char *pTmp = NULL;
	int ret = 0;

	ret = readlink("/proc/self/exe", buf, UC_BUF_LOG_LEN);
	if (ret < 0 || ret >= UC_BUF_LOG_LEN ) {
		return NULL;
	}
	buf[ret] = '\0';

	pTmp = buf + ret;
	while (pTmp >= buf) {
		if(*pTmp == '/') {
			*(pTmp + 1) = '\0';
			break;
		}
		pTmp--;
	}

	return buf;
}

char *
ucfile_get_exe_file()
{
	static char buf[UC_BUF_LOG_LEN];
	int ret = 0;

	ret = readlink("/proc/self/exe", buf, UC_BUF_LOG_LEN);
	if (ret < 0 || ret >= UC_BUF_LOG_LEN ) {
		return NULL;
	}
	buf[ret] = '\0';

	return buf;
}

void
ucfile_init()
{
	uc_strncpy(uc_path, ucfile_get_exe_path(), sizeof(uc_path));
}

/***************************************************************
 * Function: ucfile_dir
 * Created : 2015-9-22  by franco.yin
 * Description -
 * srot_by 0-name,1-creat_date
 * order 0-ordinal,1-inverted
 * external_cmd something like grep XXXX
 */
int
ucfile_dir(char *dir_name, int sort_by, int order, char *external_cmd, int count_from, int count_to, uclist_s *pList, int (*pFun)(ucfile_dir_s *, void *, void *, void *), void *p1, void *p2, void *p3)
{
	ucfile_dir_s *pDir = NULL;
	ucfile_dir_s dir_func;
	struct stat dp_st;
	char cond[10] = "-1";
	char buf[UC_BUF_LEN_100K];
	char *pTmp = NULL;
	char *p = NULL;
	int added = 0;
	int n = 0;
	int ret = 0;

	if(pList != NULL) {
		uclist_init(pList);
	}

	count_from = uc_max(1, count_from);
	count_to = uc_max(count_from, count_to);
	if(count_to - count_from >= UCFILE_DIR_FILE_MAX) {
		count_to = count_from + UCFILE_DIR_FILE_MAX - 1;
	}

	switch(sort_by) {
		case 1:
			strcat(cond, "t");
		break;
		default:
		break;
	}

	if(order == 1) {
		strcat(cond, "r");
	}

	if(external_cmd == NULL) {
		//uc_p("\nls %s %s | sed -n '%d,%dp'\n", dir_name, cond, count_from, count_to);
		uc_system_ret(buf, sizeof(buf), "ls \"%s\" %s | sed -n '%d,%dp'", dir_name, cond, count_from, count_to);
		//uc_p("\n%s\n", buf);
	} else {
		//uc_p("\nls %s %s | %s | sed -n '%d,%dp'\n", dir_name, cond, external_cmd, count_from, count_to);
		uc_system_ret(buf, sizeof(buf), "ls \"%s\" %s | %s | sed -n '%d,%dp'", dir_name, cond, external_cmd, count_from, count_to);
		//uc_p("\n%s\n", buf);
	}

	if(pList == NULL && pFun != NULL) {
		pDir = &dir_func;
	}

	p = strtok_r(buf, "\n", &pTmp);
	while(p != NULL) {
		if(pDir == NULL) {
			pDir = (ucfile_dir_s *)uc_malloc(sizeof(ucfile_dir_s));
			if(pDir == NULL) {
				p = strtok_r(NULL, "\n", &pTmp);
				continue;
			}
		}

		n = sprintf(pDir->full_path, "%s/%s", dir_name, p);
		if(stat(pDir->full_path, &dp_st) != OK) {
			p = strtok_r(NULL, "\n", &pTmp);
			continue;
		}
		pDir->creat_time = dp_st.st_mtime;
		if (S_ISDIR(dp_st.st_mode)) {
			pDir->is_folder = 1;
			pDir->full_path[n] = '/';
			pDir->full_path[n + 1] = 0;
		} else {
			pDir->is_folder = 0;
		}
		uc_strncpy(pDir->name, p, sizeof(pDir->name));

		if(pList != NULL) {
			uclist_add_tail(&(pDir->node), pList);
			pDir = NULL;
			added++;
		} else if(pFun != NULL) {
			ret = pFun(pDir, p1, p2, p3);
			if(ret == UCFILE_DIR_OK) {
				added++;
			} else if(ret == UCFILE_DIR_DROP) {
			} else{
				break;
			}
		}

		p = strtok_r(NULL, "\n", &pTmp);
	}

	if(pDir != NULL && pDir != &dir_func) {
		uc_free(pDir);
	}

	return added;
}

int
ucfile_free_dir_list(uclist_s *pList)
{
	ucfile_dir_s *pDir = NULL;
	ucnode_s *pNode = NULL;
	ucnode_s *pNode_tmp = NULL;

	uclist_for_each_safe(pNode, pNode_tmp, pList) {
		pDir = uclist_entry(pNode, ucfile_dir_s, node);
		uclist_del(pNode);
		uc_free(pDir);
	}

	return OK;
}

char *
ucfile_rename_repeat(char *folder, char *name, char *new_name)
{
	char file_name[UC_BUF_PATH_LEN];
	char file_ext[UC_BUF_PATH_LEN];
	char file_path[UC_BUF_PATH_LEN];
	int len = strlen(name);
	int i = 0;
	int flag = -1;
	int index = 0;

	snprintf(file_path, sizeof(file_path) - 1, "%s/%s", folder, name);
	if(!ucfile_exist(file_path)) {
		if(new_name != NULL) {
			strcpy(new_name, name);
			return new_name;
		} else {
			return name;
		}
	}

	for(i = len - 1; i >= 0; i--) {
		if(name[i] == '.') {
			flag = i;
			break;
		}
	}

	if(flag >= 0) {
		memcpy(file_name, name, flag);
		file_name[flag] = 0;
		memcpy(file_ext, name + flag, len - flag);
		file_ext[len - flag] = 0;
	} else {
		uc_strncpy(file_name, name, sizeof(file_name));
		file_ext[0] = 0;
	}

	while(1) {
		snprintf(file_path, sizeof(file_path) - 1, "%s/%s_%d%s", folder, file_name, index, file_ext);
		if(!ucfile_exist(file_path)) {
			if(new_name != NULL) {
				sprintf(new_name, "%s_%d%s", file_name, index, file_ext);
				return new_name;
			} else {
				sprintf(name, "%s_%d%s", file_name, index, file_ext);
				return name;
			}
		}
		index++;
	}

	return name;
}

int
ucfile_folder_size_k(char *folder)
{
	char buf[UC_BUF_LEN];

	uc_system_ret(buf, sizeof(buf), "du -s \"%s\" | awk '{print $1}'", folder);
	ucstr_trim(buf);
	return atoi(buf);
}

int
ucfile_line(char *file, int (*line_one)(char *, void *, void *, void *), void *p1, void *p2, void *p3)
{
	FILE *fp = NULL;
	char buf[UC_BUF_LEN_1K * 5];

	if(line_one == NULL) {
		return ERROR;
	}

	fp = fopen(file, "r");
	if (fp == NULL) {
		return ERROR;
	}
	rewind(fp);

	while(fgets(buf, sizeof(buf), fp) != NULL) {
		ucstr_trim(buf);
		if(line_one(buf, p1, p2, p3) != OK) {
			break;
		}
	}
	fclose(fp);

	return OK;
}

int
ucfile_append(char *file_name, char *buf, int len)
{
	int fd = 0;
	int ret = 0;

	fd = open(file_name, O_RDWR | O_CREAT | O_APPEND, 0777);
	if(fd < 0) {
		uc_err("open file[%s] error[%d:%s]\n", file_name, errno, strerror(errno));
		return ERROR;
	}

	ret = write(fd, buf, len);
	if(ret != len) {
		close(fd);
		uc_err("write file[%s] len[%d] error, ret[%d], errno[%d:%s]\n", file_name, len, ret, errno, strerror(errno));
		return ERROR;
	}

	close(fd);

	return OK;
}

int
ucfile_append_ext(char *file_name, char *head, int head_len, char *buf, int buf_len)
{
	int fd = 0;
	int ret = 0;

	fd = open(file_name, O_RDWR | O_CREAT | O_APPEND, 0777);
	if(fd < 0) {
		uc_err("open file[%s] error[%d:%s]\n", file_name, errno, strerror(errno));
		return ERROR;
	}

	ret = write(fd, head, head_len);
	if(ret != head_len) {
		close(fd);
		uc_err("write file[%s] head_len[%d] error, ret[%d], errno[%d:%s]\n", file_name, head_len, ret, errno, strerror(errno));
		return ERROR;
	}

	ret = write(fd, buf, buf_len);
	if(ret != buf_len) {
		close(fd);
		uc_err("write file[%s] len[%d] error, ret[%d], errno[%d:%s]\n", file_name, buf_len, ret, errno, strerror(errno));
		return ERROR;
	}

	close(fd);

	return OK;
}

int
ucfile_read_pos(char *file_name, UC_S64 pos, char *buf, int len)
{
	int fd = 0;
	int ret = 0;

	fd = open(file_name, O_RDONLY);
	if(fd < 0) {
		uc_err("ucfile_read_pos[%s] open error\n", file_name);
		return ERROR;
	}

	if(pos > 0) {
		lseek(fd, (off_t)pos, SEEK_SET);
	}

	ret = read(fd, buf, len);
	if(ret < 0) {
		uc_err("ucfile_read_pos[%s] read error[%d:%s]\n", file_name, errno, strerror(errno));
	}
	close(fd);

	return ret;
}

int
ucfile_write_pos(char *file_name, UC_S64 pos, char *pData, int len, int if_flush, int if_truncate)
{
	int fd = 0;
	int ret = 0;

	fd = open(file_name, O_WRONLY | O_CREAT, 0777);
	if(fd < 0) {
		return ERROR;
	}

	if(pos > 0) {
		if(pos > ucfile_size(file_name)) {
			truncate(file_name, pos);
		}
		lseek(fd, pos, SEEK_SET);
	}

	ret = write(fd, pData, len);
	if(ret >= 0) {
		pos = lseek(fd, 0, SEEK_CUR);
		if(if_truncate > 0) {
			truncate(file_name, pos);
		}
	}

	if(if_flush > 0) {
		fsync(fd);
	}

	close(fd);

	return ret;
}

int
ucfile_rlock(char *lock_file)
{
	int lock_fd = 0;
	struct flock lock ;

	lock.l_type = F_RDLCK ;
	lock.l_whence = SEEK_SET ;
	lock.l_start = 0 ;
	lock.l_len = 0 ;
	lock.l_pid = 0;

	lock_fd = open(lock_file, O_CREAT | O_RDWR, 0666);
	if(lock_fd < 0) {
		return ERROR;
	}

	fcntl(lock_fd, F_SETLKW, &lock);

	return lock_fd;
}

int
ucfile_wlock(char *lock_file)
{
	int lock_fd = 0;
	struct flock lock ;

	lock.l_type = F_WRLCK ;
	lock.l_whence = SEEK_SET ;
	lock.l_start = 0 ;
	lock.l_len = 0 ;
	lock.l_pid = 0;

	lock_fd = open(lock_file, O_CREAT | O_RDWR, 0666);
	if(lock_fd < 0) {
		return ERROR;
	}

	fcntl(lock_fd, F_SETLKW, &lock);

	return lock_fd;
}

void
ucfile_unlock(int lock_fd)
{
	if(lock_fd < 0) {
		return;
	}

	fcntl(lock_fd, F_SETLKW, F_UNLCK);
	close(lock_fd);
}

char *
ucfile_get_ext(char *src_filename, char *filename, char *extname)
{
	char *pTmp1 = NULL;
	char *pTmp2 = src_filename;

	while(*pTmp2 != 0) {
		if(*pTmp2 == '.') {
			pTmp1 = pTmp2;
		}
		pTmp2++;
	}

	if(pTmp1 == NULL) {
		if(filename != NULL) {
			memcpy(filename, src_filename, pTmp2 - src_filename);
			filename[pTmp2 - src_filename] = 0;
		}
		if(extname != NULL) {
			*extname = 0;
		}
	} else {
		if(filename != NULL) {
			memcpy(filename, src_filename, pTmp1 - src_filename);
			filename[pTmp1 - src_filename] = 0;
		}
		pTmp1++;
		if(extname != NULL) {
			memcpy(extname, pTmp1, pTmp2 - pTmp1);
			extname[pTmp2 - pTmp1] = 0;
		}
	}

	return filename;
}

int
ucfile_clean_expired_one(ucfile_dir_s *pDir, void *p1, void *p2, void *p3)
{
	char *pPath = (char *)p1;
	int *pDays = (int *)p2;
	int *pSize = (int *)p3;
	int folder_size = 0;

	if(*pDays > 0) {
		if(pDir->creat_time < *pDays) {
			uc_info("clean expire file[%s] while date[%d - %d]\n", pDir->full_path, (int)(pDir->creat_time), *pDays);
			uc_system("rm -fr \"%s\"", pDir->full_path);
			return OK;
		} else {
			*pDays = 0;
		}
	}

	if(*pSize > 0) {
		folder_size = ucfile_folder_size_k(pPath);
		if(folder_size > *pSize) {
			uc_info("clean expire file[%s] while size[%d - %d]KB\n", pDir->full_path, folder_size, *pSize);
			uc_system("rm -fr \"%s\"", pDir->full_path);
			return OK;
		} else {
			*pSize = 0;
		}
	}

	if(*pDays <= 0 && *pSize <= 0) {
		return UCFILE_DIR_BREAK;
	}

	return OK;
}

void
ucfile_clean_expired(char *dir_path, int before_days, int folder_max_size_k)
{
	uctime_sec_s now_time;
	int file_max_days = before_days;
	int file_max_size = folder_max_size_k;

	if(before_days > 0) {
		uctime_get_sec(&now_time);
		before_days = now_time - (before_days * 86400);
	}

	ucfile_dir(dir_path, 1, 1, NULL, 1, 1000, NULL, ucfile_clean_expired_one, dir_path, &file_max_days, &file_max_size);
}

#ifdef __cplusplus
}
#endif

